// SDLDrawManager.cpp: implementation of the SDLDrawManager class.
//
//////////////////////////////////////////////////////////////////////

#include "SDLDrawManager.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

SDLDrawManager::SDLDrawManager()
{

}

SDLDrawManager::~SDLDrawManager() 
{

}

 //


/*-------------------------------------------------------*/
void SDLDrawManager::bhmLine1_7(SDL_Surface *screen, SDLDrawElement * sdlElem) {
  	
	int y = sdlElem->y0;
	int x = sdlElem->x0;

	sdlElem->e = sdlElem->dy > 0 ? sdlElem->iy - sdlElem->dx : sdlElem->iy + sdlElem->dx; // 1 o 7

	for ( x = sdlElem->x0; x <= sdlElem->x1; x++)	{
		drawPoint(screen,x,y);
		sdlElem->e += sdlElem->dy;
		if (sdlElem->dy > 0) {		// 1
			if (sdlElem->e > 0) {
				y++;
				sdlElem->e -= sdlElem->dx;
				}
			}
		else {				// 7
			if (sdlElem->e < 0) {
				y--;
				sdlElem->e += sdlElem->dx;
				}
			}
		}  
	}


void SDLDrawManager::bhmLine3_5(SDL_Surface *screen, SDLDrawElement * sdlElem) {

	int y = sdlElem->y0;
	int x = sdlElem->x0;
  	
	sdlElem->e = sdlElem->dx > 0 ? sdlElem->ix + sdlElem->dy : sdlElem->ix - sdlElem->dy;	// 3 o 5
    
	for ( y = sdlElem->y0; y >= sdlElem->y1; y--)	{
		drawPoint(screen,x,y);
		sdlElem->e += sdlElem->dx;
		if (sdlElem->dx > 0) {
			if (sdlElem->e > 0) {	// 3
				x++;
				sdlElem->e += sdlElem->dy;
				}
			}
		else {				// 5
			if (sdlElem->e < 0) {
				x--;
				sdlElem->e -= sdlElem->dy;
				}
			} 
		}
	}


void SDLDrawManager::bhmScanLineFill(SDL_Surface *screen, SDLDrawElement * sdlElem)
{
int j;
  for (j = 0; j< max;j++)

	{
	  if ( (sdlElem->maxx[j] != menosinfinito) && (sdlElem->minx[j] != masinfinito) )
	  {

		  	sdlElem->x0 = sdlElem->minx[j]+1;
	        sdlElem->y0 = j;
	        sdlElem->x1 = sdlElem->maxx[j]-1;
	        sdlElem->y1 = j;

			sdlElem->dx = sdlElem->x1 - sdlElem->x0;
			sdlElem->dy = sdlElem->y1 - sdlElem->y0;
			sdlElem->ix = 2 * sdlElem->dx;
			sdlElem->iy = 2 * sdlElem->dy;
	
			if (sdlElem->dx < sdlElem->dy) {
				swapPoints(sdlElem);
				sdlElem->dx = -sdlElem->dx;
				sdlElem->dy = -sdlElem->dy;
				sdlElem->ix = -sdlElem->ix;
				sdlElem->iy = -sdlElem->iy;
			}
   
			if ( (sdlElem->dy <= -sdlElem->dx) || (sdlElem->dx < 0) )
					bhmLine3_5(screen,sdlElem);
			else
					bhmLine1_7(screen,sdlElem);


      }
  }  

}


void SDLDrawManager::swapPoints(SDLDrawElement * sdlElem) {
	int tmpX;
	int tmpY;
	
	       tmpX = sdlElem->x0;
	       tmpY = sdlElem->y0;
	sdlElem->x0 = sdlElem->x1;
	sdlElem->y0 = sdlElem->y1;
	sdlElem->x1 = tmpX;
	sdlElem->y1 = tmpY;

	}


void SDLDrawManager::bhmScanLineInit(SDL_Surface *screen, SDLDrawElement * sdlElem)
{
 
// maximo y minimo 
  int j;
  for (j = 0; j< max;j++)
  {
	  sdlElem->maxx[j] = menosinfinito;
	  sdlElem->minx[j] = masinfinito;
  }  

}

void SDLDrawManager::bhmScanLine1_7(SDL_Surface *screen,SDLDrawElement * sdlElem) {
  	
	int y = sdlElem->y0;
	int x = sdlElem->x0;

	sdlElem->e = sdlElem->dy > 0 ? sdlElem->iy - sdlElem->dx : sdlElem->iy + sdlElem->dx; // 1 o 7

	for ( x = sdlElem->x0; x <= sdlElem->x1; x++)	{
		if (x > sdlElem->maxx[y]) {sdlElem->maxx[y] = x;}
		if (x < sdlElem->minx[y]) {sdlElem->minx[y] = x;}
		drawPoint(screen,x,y);
		sdlElem->e += sdlElem->dy;
		if (sdlElem->dy > 0) {		// 1
			if (sdlElem->e > 0) {
				y++;
				sdlElem->e -= sdlElem->dx;
				}
			}
		else {				// 7
			if (sdlElem->e < 0) {
				y--;
				sdlElem->e += sdlElem->dx;
				}
			}
		}  
	}

/*--------------------------------------------------------*/
void SDLDrawManager::bhmScanLine3_5(SDL_Surface *screen, SDLDrawElement * sdlElem) {

	int y = sdlElem->y0;
	int x = sdlElem->x0;
  	
	sdlElem->e = sdlElem->dx > 0 ? sdlElem->ix + sdlElem->dy : sdlElem->ix - sdlElem->dy;	// 3 o 5

	for ( y = sdlElem->y0; y >= sdlElem->y1; y--)	{
		if (x > sdlElem->maxx[y]) {sdlElem->maxx[y] = x;}
		if (x < sdlElem->minx[y]) {sdlElem->minx[y] = x;}
		sdlElem->e += sdlElem->dx;
		if (sdlElem->dx > 0) {
			if (sdlElem->e > 0) {	// 3
				x++;
				sdlElem->e += sdlElem->dy;
				}
			}
		else {				// 5
			if (sdlElem->e < 0) {
				x--;
				sdlElem->e -= sdlElem->dy;
				}
			} 
		}
	}


void SDLDrawManager :: drawPoint(SDL_Surface *screen,int x, int y)
{
    Uint32 color = SDL_MapRGB(screen->format, cR, cG, cB);
    
	Uint32 *bufp;

	if ( SDL_MUSTLOCK(screen) ) 
    {
        if ( SDL_LockSurface(screen) < 0 ) {return;}
    }
    
	bufp = (Uint32 *)screen->pixels + y*screen->pitch/4 + x;
    *bufp = color;
    if ( SDL_MUSTLOCK(screen) ) 
    {
        
		SDL_UnlockSurface(screen);
    }

//    SDL_UpdateRect(screen, x, y, 1, 1);

}

void SDLDrawManager :: setColor(SDL_Surface * screen, Uint8 r, Uint8 g, Uint8 b)
{
	 cR = r;
	 cG = g;
	 cB = b;
}


void SDLDrawManager::bhmScanLine(SDL_Surface *screen,int X0, int Y0, int X1, int Y1, SDLDrawElement * e) {	
	
	e->x0 = X0;
	e->y0 = Y0;
	e->x1 = X1;
	e->y1 = Y1;

	e->dx = e->x1 - e->x0;
	e->dy = e->y1 - e->y0;
	e->ix = 2 * e->dx;
	e->iy = 2 * e->dy;
	
	if (e->dx <= e->dy) {
		swapPoints(e);
		e->dx = -e->dx;
		e->dy = -e->dy;
		e->ix = -e->ix;
		e->iy = -e->iy;
		}

	if ( (e->dy < -e->dx) || (e->dx < 0) )
		bhmScanLine3_5(screen,e);
	else
		bhmScanLine1_7(screen,e);
}



void SDLDrawManager::clipTexture(SDL_Surface *screen, SDLDrawElement * sdlElem, int xMin, int yMin, int h, int w)
{
	

int j;
  setColor(screen,rDefault,gDefault,bDefault);
  for (j = 0; j< max;j++)
{
	  if ( (sdlElem->maxx[j] != menosinfinito) && (sdlElem->minx[j] != masinfinito) )
	  {

		  	sdlElem->x0 = xMin;//sdlElem->minx[j];
	        sdlElem->y0 = j ;
	        sdlElem->x1 = sdlElem->minx[j];//sdlElem->maxx[j];
	        sdlElem->y1 = j ;

			sdlElem->dx = sdlElem->x1 - sdlElem->x0;
			sdlElem->dy = sdlElem->y1 - sdlElem->y0;
			sdlElem->ix = 2 * sdlElem->dx;
			sdlElem->iy = 2 * sdlElem->dy;
	
			if (sdlElem->dx < sdlElem->dy) {
				swapPoints(sdlElem);
				sdlElem->dx = -sdlElem->dx;
				sdlElem->dy = -sdlElem->dy;
				sdlElem->ix = -sdlElem->ix;
				sdlElem->iy = -sdlElem->iy;
			}
   
			if ( (sdlElem->dy <= -sdlElem->dx) || (sdlElem->dx < 0) )
					bhmLine3_5(screen,sdlElem);
			else
					bhmLine1_7(screen,sdlElem);
			

			sdlElem->x0 = sdlElem->maxx[j];//sdlElem->minx[j];
	        sdlElem->y0 = j;
	        sdlElem->x1 = xMin + w;//sdlElem->maxx[j];
	        sdlElem->y1 = j;

			sdlElem->dx = sdlElem->x1 - sdlElem->x0;
			sdlElem->dy = sdlElem->y1 - sdlElem->y0;
			sdlElem->ix = 2 * sdlElem->dx;
			sdlElem->iy = 2 * sdlElem->dy;
	
			if (sdlElem->dx < sdlElem->dy) {
				swapPoints(sdlElem);
				sdlElem->dx = -sdlElem->dx;
				sdlElem->dy = -sdlElem->dy;
				sdlElem->ix = -sdlElem->ix;
				sdlElem->iy = -sdlElem->iy;
			}
   
			if ( (sdlElem->dy <= -sdlElem->dx) || (sdlElem->dx < 0) )
					bhmLine3_5(screen,sdlElem);
			else
					bhmLine1_7(screen,sdlElem);


      }
  }  

}


void SDLDrawManager::clipCircle(SDL_Surface *screen, int radio, Point v1)
{
     int i,j;
	 int x,y;
     setColor(screen,rDefault,gDefault,bDefault);
 	 x = v1.getX();
	 y = v1.getY();
     for ( j = y-radio; j <= y+radio  ; j++)
	 for ( i = x-radio; i <= x+radio  ; i++)
	 {
		if ((sqrt((x - i)*(x - i) + (y - j)*(y - j)) > radio )) {drawPoint(screen,i,j);}
	 }



}



/*
 * Return the pixel value at (x, y)
 * NOTE: The surface must be locked before calling this!
 */
Uint32 SDLDrawManager:: getpixel(SDL_Surface *surface, int x, int y)
{
    int bpp = surface->format->BytesPerPixel;
    /* Here p is the address to the pixel we want to retrieve */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {
    case 1:
        return *p;

    case 2:
        return *(Uint16 *)p;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
            return p[0] << 16 | p[1] << 8 | p[2];
        else
            return p[0] | p[1] << 8 | p[2] << 16;

    case 4:
        return *(Uint32 *)p;

    default:
        return 0;       /* shouldn't happen, but avoids warnings */
    }
}


/*
 * Set the pixel at (x, y) to the given value
 * NOTE: The surface must be locked before calling this!
 */
void SDLDrawManager:: putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
    int bpp = surface->format->BytesPerPixel;
    /* Here p is the address to the pixel we want to set */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {
    case 1:
        *p = pixel;
        break;

    case 2:
        *(Uint16 *)p = pixel;
        break;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
            p[0] = (pixel >> 16) & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = pixel & 0xff;
        } else {
            p[0] = pixel & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = (pixel >> 16) & 0xff;
        }
        break;

    case 4:
        *(Uint32 *)p = pixel;
        break;
    }
}


void SDLDrawManager:: refresh(SDL_Surface * screen, SDL_Surface * auxSurface)
{

	 SDL_Rect dest;
	 
	 dest.x = 0;
	 dest.y = 0;
	 
	 SDL_SetColorKey (auxSurface, SDL_SRCCOLORKEY, SDL_MapRGB(auxSurface->format, rDefault, gDefault, bDefault));
	 SDL_BlitSurface(auxSurface, NULL, screen, &(dest));
	 SDL_UpdateRects(screen,1,&(dest));

	 SDL_FreeSurface(auxSurface);
	
}
void SDLDrawManager ::solveFilterColor(int r,int g, int * b)
{
	if ( (r == 0) && (g == 0) && (*b==0) ) {*b = 1;}
}


void SDLDrawManager::resizeImage(SDL_Surface * imagen, SDL_Surface * auxSurface,int x ,int y,int base, int altura, Transformations T)
{
    // si la imagen es mas grande que la figura hace la reduccion
	// si la imagen es mas chica  que la figura hace la expansion mediante el metodo del vecino mas cercano 


   	Uint32 uColor;
    double xMinT,yMinT,xMaxT,yMaxT;
	double endX,endY;
    double xt,yt;
    int k,l;
	T.doTransformation(imagen->w,imagen->h,&endX,&endY);           // esta es la posicion del pixel transformado
	
	for ( k = 0; k < imagen->w ; k++)  // recorro toda la foto en x
	for ( l = 0; l < imagen->h ; l++)  // recorro toda la foto en y
	{
 
   	  uColor = getpixel(imagen,k,l);     // este es color del pixel
	  T.doTransformation(k,l,&xt,&yt);   // esta es la posicion del pixel transformado     
	  if ( ( base > imagen->w) || (altura > imagen->h) ) {
		  
          T.doTransformation(k-1,l-1,&xMinT,&yMinT); // es el anterior transformado
		  T.doTransformation(k+1,l+1,&xMaxT,&yMaxT); // es el siguiente transformado
          expandImage(auxSurface,x,y,xt,yt,xMinT,yMinT,xMaxT,yMaxT,uColor,endX,endY); 
	  }
		// Expande cada pixel hasta la mitad de la distancia de sus vecinos
     else {putpixel(auxSurface,(double)x+(double)xt,(double)y+(double)yt,uColor);}
	}

}



void SDLDrawManager:: expandImage(SDL_Surface * surface, int  x, int  y, double xt, double yt,double xMinT,double yMinT,double xMaxT,double yMaxT, Uint32 uColor, double endX, double endY)
{

     // Metodo Sugerido Por Damian del Vecino Mas Cercano
	 // Busca un Pixel y Lo Pinta con el Color del Vecino Mas Cercano

	 int i,j;
	 //------------------------ Caso General ----------------------------------------------
	 if ( ( xt != 0 ) && ( yt != 0) )
	 {
	   for (i = ( ((xt - xMinT) / 2) + xMinT ) ; i <= ( ((xMaxT - xt) / 2) + xt )  ; i ++)
	   for (j = ( ((yt - yMinT) / 2) + yMinT ) ; j <= ( ((yMaxT - yt) / 2) + yt )  ; j ++)
	 		putpixel(surface,i+x ,j+y,uColor); 
	 }
	 
	
     //----------------------- Condiciones de Borde ---------------------------------------
     //----------------------- Los Puntos Pueden Cumplir Mas de Una -----------------------
	 if ( ( xt == 0) && ( yt != 0) )
	 {
	   for (i = 0; i <= ( ((xMaxT - xt) / 2) + xt )  ; i ++)
	   for (j = ( ((yt - yMinT) / 2) + yMinT ) ; j <= ( ((yMaxT - yt) / 2) + yt )  ; j ++)
	 		putpixel(surface,i+x ,j+y,uColor); 
	 }

	 
	 if ( ( xt != 0) && ( yt == 0) )
	 {
	   for (i = ( ((xt - xMinT) / 2) + xMinT ) ; i <= ( ((xMaxT - xt) / 2) + xt )  ; i ++)
	   for (j = 0; j <= ( ((yMaxT - yt) / 2) + yt )  ; j ++)
			putpixel(surface,i+x ,j+y,uColor); 
	 }
	
	
	 if( (xt == 0) && (yt == 0) ) 
	 {
	   for (i = 0; i <= ( ((xMaxT - xt) / 2) + xt )  ; i ++)
	   for (j = 0; j <= ( ((yMaxT - yt) / 2) + yt )  ; j ++)
			putpixel(surface,i+x ,j+y,uColor); 
	 }
	 
	
      if( (endX == xMaxT) && (endY != yMaxT)  ) 
	 {

       if ( yt != 0)
	   {
			for (i = ( ((xt - xMinT) / 2) + xMinT ) ; i < ( xMaxT  )   ; i ++)
			for (j = ( ((yt - yMinT) / 2) + yMinT ) ; j < ( ((yMaxT - yt) / 2) + yt )  ; j ++)
			putpixel(surface,i+x ,j+y,uColor);
	   }
	   else
	   {
			for (i = ( ((xt - xMinT) / 2) + xMinT ) ; i < ( xMaxT  )   ; i ++)
			for (j = 0 ; j < (yMaxT )  ; j ++)
			putpixel(surface,i+x ,j+y,uColor);
	
	   }
	 }
     
	
	 if( (endX != xMaxT) && (endY == yMaxT) ) 
	 {
       if ( xt != 0)
	   {
			for (i = ( ((xt - xMinT) / 2) + xMinT ) ; i < ( xMaxT - xt ) / 2 + xt  ; i ++)
			for (j = ( ((yt - yMinT) / 2) + yMinT ) ; j < ( yMaxT )   ; j ++)
			putpixel(surface,i+x ,j+y,uColor);
	   }
	   else
	   {
		 for (i = 0 ; i < ( xMaxT - xt ) / 2 + xt  ; i ++)
		 for (j = ( ((yt - yMinT) / 2) + yMinT ) ; j < ( yMaxT )   ; j ++)
		 putpixel(surface,i+x ,j+y,uColor);
	 
	   }
	 }
	
	 
	 if( (endX == xMaxT) && (endY == yMaxT) ) 
	 {
	   for (i = ( ((xt - xMinT) / 2) + xMinT ) ; i < ( xMaxT )   ; i ++)
	   for (j = ( ((yt - yMinT) / 2) + yMinT ) ; j < ( yMaxT )   ; j ++)
	   putpixel(surface,i+x ,j+y,uColor); 
	 }
	 	 
}


SDL_Surface* SDLDrawManager::createSurface(int w, int h, int resolution)
{
	SDL_Surface * surface;
   	SDL_Rect dest;

	surface = SDL_CreateRGBSurface(SDL_SWSURFACE,w,h,resolution,rBkDefault, gBkDefault, bBkDefault, 0);
	dest.x = 0;
	dest.y = 0;
	dest.w = surface->w;
	dest.h = surface->h;

	SDL_FillRect(surface,&dest,SDL_MapRGB(surface->format,rDefault,gDefault,bDefault));;

	
	
	return( surface );
}



